package org.wen.soft.asm;


import org.objectweb.asm.*;
import org.wen.soft.asm.model.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * ASM读取类信息
 */
public class AsmLoad {

    public static int versionASM = Opcodes.ASM5;

    /**
     * 通过文件名称读取（用于读取外部项目类class文件）
     * @param file 文件全名称
     * @return LoadClass 对象
     */
    public static LoadClass loadClass(String file)  {
        try (InputStream inputStream = Files.newInputStream(Paths.get(file))) {
            ClassReader reader = new ClassReader(inputStream);
            LoadClass loadClass = new LoadClass();
            loadClass.setFileName(file);
            return loadClass(reader, loadClass);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过流读取
     * @param inputStream 流
     * @return LoadClass 对象
     */
    public static LoadClass loadClass(InputStream inputStream)  {
        try {
            ClassReader reader  = new ClassReader(inputStream);
            return loadClass(reader);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 通过类获取
     * @param clazz 类
     * @return LoadClass 对象
     */
    public static LoadClass loadClass(Class<?> clazz) {
        try {
            ClassReader reader  = new ClassReader(clazz.getTypeName());
            return loadClass(reader);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static LoadClass loadClass(ClassReader reader){
        return loadClass(reader, new LoadClass());
    }
    /**
     * 类访问器
     */
    private static LoadClass loadClass(ClassReader reader, LoadClass loadClass) {

        ClassVisitor classVisitor = new ClassVisitor(versionASM, new ClassWriter(reader, 0)) {

            @Override // 访问类的头部
            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
                loadClass.visit(version, access, name, signature, superName, interfaces);
                super.visit(version, access, name, signature, superName, interfaces);
            }

            @Override // 访问类源文件
            public void visitSource(String source, String debug) {
                loadClass.visitSource(source, debug);
                super.visitSource(source, debug);
            }

            @Override // todo 访问 Module 模块， Java9 中新增关键字 module 用于定义代码和数据的封装体；
            public ModuleVisitor visitModule(String name, int access, String version) {
                System.out.println("visitModule: " + name + " " + access + " " + version);
                return super.visitModule(name, access, version);
            }

            @Override // todo 访问嵌套类
            public void visitNestHost(String nestHost) {
                System.out.println("nestHost: " + nestHost);
                super.visitNestHost(nestHost);
            }

            @Override // 访问匿名对象： 所在类、所在方法名、所在方法参数信息
            public void visitOuterClass(String owner, String name, String descriptor) {
                super.visitOuterClass(owner, name, descriptor);
            }

            @Override // 访问类的注解
            public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                LoadAnnotation loadAnnotation = new LoadAnnotation(descriptor, visible);
                loadClass.visitAnnotation(loadAnnotation);
                return visitAnnotationASM(loadAnnotation, super.visitAnnotation(descriptor, visible));
            }

            @Override // todo 访问类的泛型签名的注解
            public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
                System.out.println("visitTypeAnnotation: " + typeRef + " " + typePath + " " + descriptor + " " + visible);
                return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
            }

            @Override
            public void visitAttribute(Attribute attribute) {
                System.out.println("visitAttribute > attribute = " + attribute);
                super.visitAttribute(attribute);
            }

            @Override
            public void visitNestMember(String nestMember) {
                System.out.println("visitNestMember > nestMember = " + nestMember);
                super.visitNestMember(nestMember);
            }

            @Override
            public void visitPermittedSubclass(String permittedSubclass) {
                System.out.println("visitPermittedSubclass > permittedSubclass = " + permittedSubclass);
                super.visitPermittedSubclass(permittedSubclass);
            }

            @Override // 访问内部类
            public void visitInnerClass(String name, String outerName, String innerName, int access) {
                loadClass.visitInnerClass(new LoadInnerClass(access, name, outerName, innerName));
                super.visitInnerClass(name, outerName, innerName, access);
            }

            @Override
            public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
                System.out.println("visitRecordComponent > name = " + name + ", descriptor = " + descriptor + ", signature = " + signature);
                return super.visitRecordComponent(name, descriptor, signature);
            }

            @Override // 访问类的字段
            public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
                LoadField loadField = new LoadField(access, name, descriptor, signature, value);
                loadClass.visitField(loadField);
                return visitFieldASM(loadField, super.visitField(access, name, descriptor, signature, value));
            }

            @Override // 访问类的方法
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                LoadMethod loadMethod = new LoadMethod(access, name, descriptor, signature, exceptions);
                loadClass.visitMethod(loadMethod);
                return visitMethodASM(loadMethod, super.visitMethod(access, name, descriptor, signature, exceptions));
            }
        };
        reader.accept(classVisitor, versionASM);
        return loadClass;
    }

    /**
     * 注解访问器
     */
    private static AnnotationVisitor visitAnnotationASM(LoadAnnotation loadAnnotation, AnnotationVisitor visitor) {
        return new AnnotationVisitor(versionASM, visitor) {
            @Override
            public void visit(String name, Object value) {
                loadAnnotation.addKeyValue(name, value);
                super.visit(name, value);
            }
        };
    }

    /**
     * 字段访问器
     */
    private static FieldVisitor visitFieldASM(LoadField loadField, FieldVisitor visitor) {
        return new FieldVisitor(versionASM, visitor) {
            @Override
            public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                LoadAnnotation loadAnnotation = new LoadAnnotation(descriptor, visible);
                loadField.addLoadAnnotation(loadAnnotation);
                return visitAnnotationASM(loadAnnotation, super.visitAnnotation(descriptor, visible));
            }
        };
    }

    /**
     * 方法访问器
     */
    private static MethodVisitor visitMethodASM(LoadMethod loadMethod, MethodVisitor visitor) {
        return new MethodVisitor(versionASM, visitor) {
            @Override
            public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                LoadAnnotation loadAnnotation = new LoadAnnotation(descriptor, visible);
                loadMethod.visitAnnotation(loadAnnotation);
                return visitAnnotationASM(loadAnnotation, super.visitAnnotation(descriptor, visible));
            }

            @Override
            public void visitParameter(String name, int access) {
                loadMethod.visitParameter(name, access);
                super.visitParameter(name, access);
            }

            @Override
            public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
                loadMethod.visitInvokeMethod(new LoadInvokeMethod(opcode, owner, name, descriptor, isInterface));
                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
            }

            @Override
            public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) {
                LoadAnnotation loadAnnotation = new LoadAnnotation(descriptor, visible);
                loadMethod.visitParameterAnnotation(parameter, loadAnnotation);
                return visitAnnotationASM(loadAnnotation, super.visitParameterAnnotation(parameter, descriptor, visible));
            }

        };
    }
}
